package ru.averta.notes.dao.impl;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ru.averta.notes.dao.GenericDao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

/**
 * Hibernate implementation of GenericDao
 * A typesafe implementation of CRUD and finder methods based on Hibernate and Spring AOP
 * The finders are implemented through the executeFinder method. Normally called by the FinderIntroductionInterceptor
 */
public class GenericDaoHibernateImpl<T, PK extends Serializable> implements GenericDao<T, PK> {

    Logger logger = LoggerFactory.getLogger(GenericDaoHibernateImpl.class);
    private Class<T> domainClass;

    @Autowired
    private SessionFactory sessionFactory;

    public GenericDaoHibernateImpl() { }
    public GenericDaoHibernateImpl(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }

    @SuppressWarnings("unchecked")
    public PK create(T o) {
        return (PK) sessionFactory.getCurrentSession().save(o);
    }

    @SuppressWarnings("unchecked")
    public T findByID(PK id) {
        return (T) sessionFactory.getCurrentSession().get(getDomainClass(), id);
    }

    public void save(T o) {
        sessionFactory.getCurrentSession().saveOrUpdate(o);
    }

    public void remove(T o) {
        sessionFactory.getCurrentSession().delete(o);
    }

    public void remove(PK id) {
        remove(findByID(id));
    }

    @SuppressWarnings("unchecked")
    private Class<T> getDomainClass() {
        if (domainClass == null) {
            ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass();
            this.domainClass = (Class<T>) thisType.getActualTypeArguments()[0];
        }
        return domainClass;
    }

    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        return createCriteria().list();
    }

    @SuppressWarnings("unchecked")
    public List<T> findAllByProperty(String propertyName, Object value) {
        return createCriteria().add(Restrictions.eq(propertyName, value)).list();
    }

    protected Criteria createCriteria() {
        return createCriteria(getDomainClass(), null);
    }

    protected Criteria createCriteria(String alias) {
        return createCriteria(getDomainClass(), alias);
    }

    protected Criteria createCriteria(Class persistentClass, String alias) {
        Criteria criteria = null;
        if (alias == null) {
            criteria = sessionFactory.getCurrentSession().createCriteria(persistentClass);
        } else {
            criteria = sessionFactory.getCurrentSession().createCriteria(persistentClass, alias);
        }
            //if (IArchivable.class.isAssignableFrom(persistentClass)) criteria.add(Restrictions.eq("trashed", false));
        return criteria;
    }

    protected Query createQuery(String query) {
        return sessionFactory.getCurrentSession().createQuery(query);
    }

    protected SQLQuery createSqlQuery(String query) {
        return sessionFactory.getCurrentSession().createSQLQuery(query);
    }
}
